/*-------------------------------------------------------------------------
 *
 * operatorcmds.c
 *
 *	  操作符操作命令的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/operatorcmds.c
 *
 * DESCRIPTION
 *	  “DefineFoo” 例程接受解析树并提取适当的参数/标志，将结果传递给
 *	  进行实际目录处理的相应“FooDefine” 例程（在 src/catalog 中）。
 *	  这些例程还验证用户执行命令的权限。
 *
 * NOTES
 *	  这些内容必须按以下顺序定义并提交：
 *		“create function”：
 *				input/output, recv/send 函数
 *		“create type”：
 *				类型
 *		“create operator”：
 *				操作符
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"

static Oid	fc_ValidateRestrictionEstimator(List *fc_restrictionName);
static Oid	fc_ValidateJoinEstimator(List *fc_joinName);

/*
 * DefineOperator
 *		这个函数从解析器生成的参数列表中提取所有信息，然后让
 *		OperatorCreate() 进行所有实际工作。
 *
 * 'parameters' 是一个 DefElem 列表
 */
ObjectAddress DefineOperator(List *fc_names, List *fc_parameters)
{
	char	   *fc_oprName;
	Oid			fc_oprNamespace;
	AclResult	fc_aclresult;
	bool		fc_canMerge = false;	/* 操作符合并 */
	bool		fc_canHash = false;	/* 操作符哈希 */
	List	   *fc_functionName = NIL; /* 操作符的函数 */
	TypeName   *fc_typeName1 = NULL;	/* 第一个类型名称 */
	TypeName   *fc_typeName2 = NULL;	/* 第二个类型名称 */
	Oid			fc_typeId1 = InvalidOid;	/* 类型转换为 OID */
	Oid			fc_typeId2 = InvalidOid;
	Oid			fc_rettype;
	List	   *fc_commutatorName = NIL;	/* 可选的可交换操作符名称 */
	List	   *fc_negatorName = NIL;	/* 可选的取反操作符名称 */
	List	   *fc_restrictionName = NIL;	/* 可选的限制选择函数 */
	List	   *fc_joinName = NIL; /* 可选的连接选择函数 */
	Oid			fc_functionOid;	/* 函数转换为 OID */
	Oid			fc_restrictionOid;
	Oid			fc_joinOid;
	Oid			fc_typeId[2];		/* 用来保存左侧和右侧参数 */
	int			fc_nargs;
	ListCell   *fc_pl;

	/* 将名称列表转换为一个名称和命名空间 */
	fc_oprNamespace = QualifiedNameGetCreationNamespace(fc_names, &fc_oprName);

	/* 检查我们在目标命名空间中是否有创建权限 */
	fc_aclresult = pg_namespace_aclcheck(fc_oprNamespace, GetUserId(), ACL_CREATE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_SCHEMA,
					   get_namespace_name(fc_oprNamespace));

	/*
	 * 遍历定义列表并提取我们所需的信息。
	 */
	foreach(fc_pl, fc_parameters)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_pl);

		if (strcmp(fc_defel->defname, "leftarg") == 0)
		{
			fc_typeName1 = defGetTypeName(fc_defel);
			if (fc_typeName1->setof)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("SETOF type not allowed for operator argument")));
		}
		else if (strcmp(fc_defel->defname, "rightarg") == 0)
		{
			fc_typeName2 = defGetTypeName(fc_defel);
			if (fc_typeName2->setof)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("SETOF type not allowed for operator argument")));
		}
		/* 在这里，“函数”和“过程”是等价的 */
		else if (strcmp(fc_defel->defname, "function") == 0)
			fc_functionName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "procedure") == 0)
			fc_functionName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "commutator") == 0)
			fc_commutatorName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "negator") == 0)
			fc_negatorName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "restrict") == 0)
			fc_restrictionName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "join") == 0)
			fc_joinName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "hashes") == 0)
			fc_canHash = defGetBoolean(fc_defel);
		else if (strcmp(fc_defel->defname, "merges") == 0)
			fc_canMerge = defGetBoolean(fc_defel);
		/* 这些过时的选项被理解为可以合并 */
		else if (strcmp(fc_defel->defname, "sort1") == 0)
			fc_canMerge = true;
		else if (strcmp(fc_defel->defname, "sort2") == 0)
			fc_canMerge = true;
		else if (strcmp(fc_defel->defname, "ltcmp") == 0)
			fc_canMerge = true;
		else if (strcmp(fc_defel->defname, "gtcmp") == 0)
			fc_canMerge = true;
		else
		{
			/* 警告，而不是错误，为历史向后兼容性考虑 */
			ereport(WARNING,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("operator attribute \"%s\" not recognized",
							fc_defel->defname)));
		}
	}

	/*
	 * 确保我们有所需的定义
	 */
	if (fc_functionName == NIL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("operator function must be specified")));

	/* 将类型名称转换为类型 OID */
	if (fc_typeName1)
		fc_typeId1 = typenameTypeId(NULL, fc_typeName1);
	if (fc_typeName2)
		fc_typeId2 = typenameTypeId(NULL, fc_typeName2);

	/*
	 * 如果只有右侧参数缺失，用户很可能试图创建一个后缀操作符，
	 * 所以给他们一个关于为什么这不工作的提示。但如果两个参数都缺失，
	 * 不要提及后缀操作符，因为用户很可能只是忽略了提及参数。
	 */
	if (!OidIsValid(fc_typeId1) && !OidIsValid(fc_typeId2))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("operator argument types must be specified")));
	if (!OidIsValid(fc_typeId2))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("operator right argument type must be specified"),
				 errdetail("Postfix operators are not supported.")));

	if (fc_typeName1)
	{
		fc_aclresult = pg_type_aclcheck(fc_typeId1, GetUserId(), ACL_USAGE);
		if (fc_aclresult != ACLCHECK_OK)
			aclcheck_error_type(fc_aclresult, fc_typeId1);
	}

	if (fc_typeName2)
	{
		fc_aclresult = pg_type_aclcheck(fc_typeId2, GetUserId(), ACL_USAGE);
		if (fc_aclresult != ACLCHECK_OK)
			aclcheck_error_type(fc_aclresult, fc_typeId2);
	}

	/*
	 * 查找操作符的基础函数。
	 */
	if (!OidIsValid(fc_typeId1))
	{
		fc_typeId[0] = fc_typeId2;
		fc_nargs = 1;
	}
	else if (!OidIsValid(fc_typeId2))
	{
		fc_typeId[0] = fc_typeId1;
		fc_nargs = 1;
	}
	else
	{
		fc_typeId[0] = fc_typeId1;
		fc_typeId[1] = fc_typeId2;
		fc_nargs = 2;
	}
	fc_functionOid = LookupFuncName(fc_functionName, fc_nargs, fc_typeId, false);

	/*
	 * 我们需要该函数的 EXECUTE 权限。这严格来说并不是必要的，
	 * 因为在任何尝试使用该操作符时都会检查 EXECUTE 权限，
	 * 但这样做似乎还是个好主意。
	 */
	fc_aclresult = pg_proc_aclcheck(fc_functionOid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION,
					   NameListToString(fc_functionName));

	fc_rettype = get_func_rettype(fc_functionOid);
	fc_aclresult = pg_type_aclcheck(fc_rettype, GetUserId(), ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error_type(fc_aclresult, fc_rettype);

	/*
	 * 如果指定，查找限制和连接估算器
	 */
	if (fc_restrictionName)
		fc_restrictionOid = fc_ValidateRestrictionEstimator(fc_restrictionName);
	else
		fc_restrictionOid = InvalidOid;
	if (fc_joinName)
		fc_joinOid = fc_ValidateJoinEstimator(fc_joinName);
	else
		fc_joinOid = InvalidOid;

	/*
	 * 现在让 OperatorCreate 做所有的工作。
	 */
	return
		OperatorCreate(fc_oprName, /* 操作符名称 */
					   fc_oprNamespace,	/* 名称空间 */
					   fc_typeId1, /* 左侧类型 ID */
					   fc_typeId2, /* 右侧类型 ID */
					   fc_functionOid, /* 操作符的函数 */
					   fc_commutatorName,	/* 可选的可交换操作符名称 */
					   fc_negatorName, /* 可选的取反操作符名称 */
					   fc_restrictionOid,	/* 可选的限制选择函数 */
					   fc_joinOid, /* 可选的连接选择函数名称 */
					   fc_canMerge,	/* 操作符合并 */
					   fc_canHash);	/* 操作符哈希 */
}

/*
 * 通过名称查找限制估算器函数，并验证它具有
 * 正确的签名，并且我们有权限将其附加到操作符。
 */
static Oid fc_ValidateRestrictionEstimator(List *fc_restrictionName)
{
	Oid			fc_typeId[4];
	Oid			fc_restrictionOid;
	AclResult	fc_aclresult;

	fc_typeId[0] = INTERNALOID;	/* PlannerInfo */
	fc_typeId[1] = OIDOID;			/* 操作符 OID */
	fc_typeId[2] = INTERNALOID;	/* 参数列表 */
	fc_typeId[3] = INT4OID;		/* varRelid */

	fc_restrictionOid = LookupFuncName(fc_restrictionName, 4, fc_typeId, false);

	/* 估算器必须返回 float8 */
	if (get_func_rettype(fc_restrictionOid) != FLOAT8OID)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("restriction estimator function %s must return type %s",
						NameListToString(fc_restrictionName), "float8")));

	/* 对于估算器要求 EXECUTE 权限 */
	fc_aclresult = pg_proc_aclcheck(fc_restrictionOid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION,
					   NameListToString(fc_restrictionName));

	return fc_restrictionOid;
}

/*
 * 通过名称查找连接估算器函数，并验证它具有
 * 正确的签名，并且我们有权限将其附加到操作符。
 */
static Oid fc_ValidateJoinEstimator(List *fc_joinName)
{
	Oid			fc_typeId[5];
	Oid			fc_joinOid;
	Oid			fc_joinOid2;
	AclResult	fc_aclresult;

	fc_typeId[0] = INTERNALOID;	/* PlannerInfo */
	fc_typeId[1] = OIDOID;			/* 操作符 OID */
	fc_typeId[2] = INTERNALOID;	/* 参数列表 */
	fc_typeId[3] = INT2OID;		/* 连接类型 */
	fc_typeId[4] = INTERNALOID;	/* SpecialJoinInfo */

	/*
	 * 从 Postgres 8.4 开始，连接估算器的首选签名有 5 个参数，但我们仍然允许旧的 4 个参数形式。如果两种形式都存在，则会产生歧义。
	 */
	fc_joinOid = LookupFuncName(fc_joinName, 5, fc_typeId, true);
	fc_joinOid2 = LookupFuncName(fc_joinName, 4, fc_typeId, true);
	if (OidIsValid(fc_joinOid))
	{
		if (OidIsValid(fc_joinOid2))
			ereport(ERROR,
					(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
					 errmsg("join estimator function %s has multiple matches",
							NameListToString(fc_joinName))));
	}
	else
	{
		fc_joinOid = fc_joinOid2;
		/* 如果未找到，请在错误消息中引用 5 个参数的签名 */
		if (!OidIsValid(fc_joinOid))
			fc_joinOid = LookupFuncName(fc_joinName, 5, fc_typeId, false);
	}

	/* 估算器必须返回 float8 */
	if (get_func_rettype(fc_joinOid) != FLOAT8OID)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("join estimator function %s must return type %s",
						NameListToString(fc_joinName), "float8")));

	/* 对于估算器要求 EXECUTE 权限 */
	fc_aclresult = pg_proc_aclcheck(fc_joinOid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION,
					   NameListToString(fc_joinName));

	return fc_joinOid;
}

/*
 * 操作符删除的核心部分。
 */
void RemoveOperatorById(Oid fc_operOid)
{
	Relation	fc_relation;
	HeapTuple	fc_tup;
	Form_pg_operator fc_op;

	fc_relation = table_open(OperatorRelationId, RowExclusiveLock);

	fc_tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_operOid));
	if (!HeapTupleIsValid(fc_tup)) /* 不应该发生 */
		elog(ERROR, "cache lookup failed for operator %u", fc_operOid);
	fc_op = (Form_pg_operator) GETSTRUCT(fc_tup);

	/*
	 * 重置来自可交换符和取反符的链接（如果有）。如果是自可交换符或自取反符，这意味着我们必须重新获取更新的元组。（我们可以优化一下即将删除的元组上的更新，但似乎不值得复杂化逻辑。）
	 */
	if (OidIsValid(fc_op->oprcom) || OidIsValid(fc_op->oprnegate))
	{
		OperatorUpd(fc_operOid, fc_op->oprcom, fc_op->oprnegate, true);
		if (fc_operOid == fc_op->oprcom || fc_operOid == fc_op->oprnegate)
		{
			ReleaseSysCache(fc_tup);
			fc_tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_operOid));
			if (!HeapTupleIsValid(fc_tup)) /* 不应该发生 */
				elog(ERROR, "cache lookup failed for operator %u", fc_operOid);
		}
	}

	CatalogTupleDelete(fc_relation, &fc_tup->t_self);

	ReleaseSysCache(fc_tup);

	table_close(fc_relation, RowExclusiveLock);
}

/*
 * AlterOperator
 *		实现 ALTER OPERATOR <operator> SET (option = ...) 的例程。
 *
 * 目前，仅 RESTRICT 和 JOIN 估算器函数可以被更改。
 */
ObjectAddress AlterOperator(AlterOperatorStmt *fc_stmt)
{
	ObjectAddress fc_address;
	Oid			fc_oprId;
	Relation	fc_catalog;
	HeapTuple	fc_tup;
	Form_pg_operator fc_oprForm;
	int			fc_i;
	ListCell   *fc_pl;
	Datum		fc_values[Natts_pg_operator];
	bool		fc_nulls[Natts_pg_operator];
	bool		fc_replaces[Natts_pg_operator];
	List	   *fc_restrictionName = NIL;	/* 可选的限制选择函数 */
	bool		fc_updateRestriction = false;
	Oid			fc_restrictionOid;
	List	   *fc_joinName = NIL; /* 可选的连接选择函数 */
	bool		fc_updateJoin = false;
	Oid			fc_joinOid;

	/* 查找操作符 */
	fc_oprId = LookupOperWithArgs(fc_stmt->opername, false);
	fc_catalog = table_open(OperatorRelationId, RowExclusiveLock);
	fc_tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(fc_oprId));
	if (!HeapTupleIsValid(fc_tup))
		elog(ERROR, "cache lookup failed for operator %u", fc_oprId);
	fc_oprForm = (Form_pg_operator) GETSTRUCT(fc_tup);

	/* 处理选项 */
	foreach(fc_pl, fc_stmt->options)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_pl);
		List	   *fc_param;

		if (fc_defel->arg == NULL)
			fc_param = NIL;		/* NONE，移除该函数 */
		else
			fc_param = defGetQualifiedName(fc_defel);

		if (strcmp(fc_defel->defname, "restrict") == 0)
		{
			fc_restrictionName = fc_param;
			fc_updateRestriction = true;
		}
		else if (strcmp(fc_defel->defname, "join") == 0)
		{
			fc_joinName = fc_param;
			fc_updateJoin = true;
		}

		/*
		 * CREATE 接受的其余选项不能被更改。
		 * 检查它们，以便我们可以给出有意义的错误消息。
		 */
		else if (strcmp(fc_defel->defname, "leftarg") == 0 ||
				 strcmp(fc_defel->defname, "rightarg") == 0 ||
				 strcmp(fc_defel->defname, "function") == 0 ||
				 strcmp(fc_defel->defname, "procedure") == 0 ||
				 strcmp(fc_defel->defname, "commutator") == 0 ||
				 strcmp(fc_defel->defname, "negator") == 0 ||
				 strcmp(fc_defel->defname, "hashes") == 0 ||
				 strcmp(fc_defel->defname, "merges") == 0)
		{
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("operator attribute \"%s\" cannot be changed",
							fc_defel->defname)));
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("operator attribute \"%s\" not recognized",
							fc_defel->defname)));
	}

	/* 检查权限。必须是所有者。 */
	if (!pg_oper_ownercheck(fc_oprId, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
					   NameStr(fc_oprForm->oprname));

	/*
	 * 如果指定，查找限制和连接估算器
	 */
	if (fc_restrictionName)
		fc_restrictionOid = fc_ValidateRestrictionEstimator(fc_restrictionName);
	else
		fc_restrictionOid = InvalidOid;
	if (fc_joinName)
		fc_joinOid = fc_ValidateJoinEstimator(fc_joinName);
	else
		fc_joinOid = InvalidOid;

	/* 执行额外检查，如 OperatorCreate 所做的那样 */
	if (!(OidIsValid(fc_oprForm->oprleft) && OidIsValid(fc_oprForm->oprright)))
	{
		/* 如果它不是二元操作，这些内容不能被设置： */
		if (OidIsValid(fc_joinOid))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only binary operators can have join selectivity")));
	}

	if (fc_oprForm->oprresult != BOOLOID)
	{
		if (OidIsValid(fc_restrictionOid))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can have restriction selectivity")));
		if (OidIsValid(fc_joinOid))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can have join selectivity")));
	}

	/* 更新元组 */
	for (fc_i = 0; fc_i < Natts_pg_operator; ++fc_i)
	{
		fc_values[fc_i] = (Datum) 0;
		fc_replaces[fc_i] = false;
		fc_nulls[fc_i] = false;
	}
	if (fc_updateRestriction)
	{
		fc_replaces[Anum_pg_operator_oprrest - 1] = true;
		fc_values[Anum_pg_operator_oprrest - 1] = fc_restrictionOid;
	}
	if (fc_updateJoin)
	{
		fc_replaces[Anum_pg_operator_oprjoin - 1] = true;
		fc_values[Anum_pg_operator_oprjoin - 1] = fc_joinOid;
	}

	fc_tup = heap_modify_tuple(fc_tup, RelationGetDescr(fc_catalog),
							fc_values, fc_nulls, fc_replaces);

	CatalogTupleUpdate(fc_catalog, &fc_tup->t_self, fc_tup);

	fc_address = makeOperatorDependencies(fc_tup, false, true);

	InvokeObjectPostAlterHook(OperatorRelationId, fc_oprId, 0);

	table_close(fc_catalog, NoLock);

	return fc_address;
}
